#include <54func.h>
typedef struct node_s{
    int date;
    struct node_s * pNext;
}node_t;
typedef struct queue_s{
    node_t *pFront;//队头节点
    node_t *pRear;//队尾节点
    int queueSize;//队大小
}queue_t;
typedef struct shareres_s{
    queue_t queue;//队列现在也是生产者与消费者之间的共享资源
    pthread_mutex_t mutex;
    pthread_cond_t cond;
}shareRes_t;
//尾插法入队
int enQueue(queue_t *pQueue,int data)
{
    node_t * pNew = (node_t *)malloc(sizeof(node_t));
    memset(pNew,0,sizeof(node_t));//将申请了结点指针指向NULL
    pNew->date = data;//数据域为传进来的数据
    if(pQueue->queueSize == 0)//如果原来队列大小为0
    {
        pQueue->pFront = pNew;
        pQueue->pRear = pNew;
    }else
    {
        pQueue->pRear->pNext = pNew;
        pQueue->pRear = pNew;
    }
    ++pQueue->queueSize;
    return 0;
}
int inQueue(queue_t *pQueue)
{
    //pCur是要删除的结点指针
    node_t *pCur = pQueue->pFront;
    pQueue->pFront = pCur->pNext;
    //若删除的是唯一一个节点，则要修改队尾指针指向
    if(pQueue->queueSize == 1)
    {
        pQueue->pRear = NULL;
    }
    free(pCur);
    --pQueue->queueSize;
    return 0;
}
//访问队列
int visit(queue_t *pQueue)
{
    node_t * pCur = pQueue->pFront;
    while(pCur)
    {
        printf("%d ",pCur->date);
        pCur = pCur->pNext;
    }
    printf("\n");
    return 0;
}
//int main()
//{
//    queue_t myQueue;
//    memset(&myQueue,0,sizeof(myQueue));
//    for(int i = 0;i<10;i++)//入队测试
//    {
//        int data = rand()%1000;
//        enQueue(&myQueue,data);
//        printf("data = %d\n",data);
//        visit(&myQueue);
//    }
//    printf("-------------------------------------\n");
//    for(int i = 0;i<10;i++)//出队测试
//    {
//        //一定要先获取队头元素->再出队
//        printf("data = %d\n",myQueue.pFront->date);//先输出队头元素
//        inQueue(&myQueue);//出队，队头元素已经不在对头了
//        visit(&myQueue);//在打印
//    }
//    return 0;
//}
void *producer(void *arg)
{
    //消费者：在生产者与消费者模型中，即是先事件，也是后事件，因此我们先执行后事件
    shareRes_t * pshareRes = (shareRes_t *)arg;
    while(1){
        pthread_mutex_lock(&pshareRes->mutex);
        while(pshareRes->queue.queueSize >= 10)
        {
            //若是资源大于10个，则生产者不能生产商品
            pthread_cond_wait(&pshareRes->cond,&pshareRes->mutex);
        }
        //此刻商品数目是小于10，生产者生产商品（入队）
        printf("before producer,current queuesuzei = %d\n",pshareRes->queue.queueSize);//输出当前队列元素个数
        int data = rand()%1000;
        printf("data = %d\n",data);//打印一下入队袁术
        enQueue(&pshareRes->queue,data);
        visit(&pshareRes->queue);//打印队列
        printf("after producer,current queuesuzei = %d\n",pshareRes->queue.queueSize);
        
        //生产者生产商品结束后，就需要发消息给消费者，让其消费
        pthread_cond_broadcast(&pshareRes->cond);
        pthread_mutex_unlock(&pshareRes->mutex);//解锁

        sleep(3);
    }
    pthread_exit(NULL);
}
void *comsumer(void *arg)
{
    sleep(5);
    shareRes_t * pshareRes = (shareRes_t *)arg;
    while(1){//多线程同步，while可用于虚唤醒
        pthread_mutex_lock(&pshareRes->mutex);
        while(pshareRes->queue.queueSize <= 0)
        {
            //若是资源小于0，则消费者不能消费商品->等待
            pthread_cond_wait(&pshareRes->cond,&pshareRes->mutex);

        }
        //此刻上哦数目一定是大于0个，消费者消费商品（出队）
        printf("before comsumer,current queuesuzei = %d\n",pshareRes->queue.queueSize);
        int front = pshareRes->queue.pFront->date;//打印一下出队的元素
        printf("fornt = %d\n",front);
        inQueue(&pshareRes->queue);//出队
        printf("after comsumer,current queuesuzei = %d\n",pshareRes->queue.queueSize);

        //消费者消费结束后，则向生产者队列发送广播，让其生产
        pthread_cond_broadcast(&pshareRes->cond);//信息可以在锁内，也可以在锁外
        pthread_mutex_unlock(&pshareRes->mutex);

        sleep(1);//每过一秒消费一个产品
    }
    pthread_exit(NULL);
}
int main()
{
    shareRes_t shareRes;
    memset(&shareRes.queue,0,sizeof(shareRes.queue));//队列初始化
    pthread_mutex_init(&shareRes.mutex,NULL);
    pthread_cond_init(&shareRes.cond,NULL);
    for(int i=0;i<8;i++)
    {
        //入队，尾差法
        int data = rand()%1000;
        printf("data = %d\n",data);
        enQueue(&shareRes.queue,data);
        visit(&shareRes.queue);//打印队列
    }

    pthread_t tid1,tid2,tid3,tid4,tid5;
    pthread_create(&tid1,NULL,producer,&shareRes);
    pthread_create(&tid2,NULL,producer,&shareRes);
    pthread_create(&tid3,NULL,producer,&shareRes);
    pthread_create(&tid4,NULL,comsumer,&shareRes);
    pthread_create(&tid5,NULL,comsumer,&shareRes);

    pthread_join(tid1,NULL);
    pthread_join(tid2,NULL);
    pthread_join(tid3,NULL);
    pthread_join(tid4,NULL);
    pthread_join(tid5,NULL);
    return 0;
}
